﻿using System.Text.Json.Serialization;

using IGeekFan.AspNetCore.Knife4jUI;

using Microsoft.AspNetCore.Http.Extensions;
using Microsoft.AspNetCore.Http.Json;
using Microsoft.AspNetCore.HttpLogging;
using Microsoft.AspNetCore.Mvc;
using Microsoft.OpenApi.Models;

using Serilog;
using Serilog.Core.Enrichers;
using Serilog.Enrichers.AspNetCore.HttpContext;

using JackProjectTemplate.Ability;
using JackProjectTemplate.Http.Client;
using JackProjectTemplate.Shared.Extensions;
using JackProjectTemplate.Shared.Utilities;
using JackProjectTemplate.Storage;
using JackProjectTemplate.Web.Filters;

using Youshow.Ace;
using Youshow.Ace.AspNetCore.Web;
using Youshow.Ace.AspNetCore.Web.Conventions;
using Youshow.Ace.Modularity;

namespace JackProjectTemplate.Web
{
    [RelyOn(
        typeof(AceAspNetCoreWebModule),
        typeof(JackProjectTemplateHttpClientModule),
        typeof(JackProjectTemplateAbilityModule),
        typeof(JackProjectTemplateStorageModule)
    )]
    public class JackProjectTemplateWebModule : AceModule
    {
        public override void ConfigureServices(ServiceConfigurationContext context)
        {
            ArgumentNullException.ThrowIfNull(context);
            //ConfigurationManager? configurationManager = context.Services.FirstOrDefault(x => x.ServiceType == typeof(IConfiguration))?.ImplementationInstance as ConfigurationManager;
            //ArgumentNullException.ThrowIfNull(configurationManager);
            ConfigurationManager configurationManager = context.Services.GetConfigurationManager();

            //IWebHostEnvironment? webHostEnvironment = context.Services.FirstOrDefault(x => x.ServiceType == typeof(IWebHostEnvironment))?.ImplementationInstance as IWebHostEnvironment;
            //ArgumentNullException.ThrowIfNull(webHostEnvironment);
            IWebHostEnvironment webHostEnvironment = context.Services.GetRequiredService<IWebHostEnvironment>();

            // Add services to the container.
            context.Services.AddControllers();

            // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
            context.Services.AddEndpointsApiExplorer();
            context.Services.AddCors(c => c.AddDefaultPolicy(p => p.AllowAnyHeader().AllowAnyMethod().AllowCredentials().SetIsOriginAllowed(origin => true)));

            if (!webHostEnvironment.IsProduction())
            {
                context.Services.AddProblemDetails();
                context.Services.Configure<RouteHandlerOptions>(opts =>
                {
                    opts.ThrowOnBadRequest = true;
                });
                context.Services.AddEndpointsApiExplorer();
                context.Services.AddSwaggerGen(c =>
                {
                    c.SwaggerDoc("JackProjectTemplateV1", new OpenApiInfo { Title = "JackProjectTemplate-接口", Version = "JackProjectTemplateV1" });
                    // 在swagger中显示Application中暴露的接口
                    c.DocInclusionPredicate((_, _) => true);
                    c.CustomSchemaIds(t => t.FullName?.Replace("+", "$"));

                    // 添加安全定义
                    c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
                    {
                        Description = "JWT授权(数据将在请求头中进行传递)直接在下面框中输入Bearer {token}(注意两者之间是一个空格)",
                        Name = "Authorization", // jwt默认的参数名称
                        In = ParameterLocation.Header, // jwt默认存放Authorization信息的位置(请求头中)
                        Type = SecuritySchemeType.ApiKey,
                        BearerFormat = "JWT",
                        Scheme = "Bearer"
                    });

                    c.OperationFilter<SwaggerOperationFilter>();
                    c.DocumentFilter<SwaggerDocumentFilter>([]);

                    foreach (var docXml in Directory.GetFiles(AppContext.BaseDirectory, "*.xml", SearchOption.TopDirectoryOnly))
                    {
                        c.IncludeXmlComments(docXml, includeControllerXmlComments: true);
                    }
                });

                Configure<MvcOptions>(opts =>
                {
                    opts.Filters.Add<DiagnosticErrorFilter>(1000);
                });
            }
            Configure<AceAspNetCoreWebOptions>(opts =>
            {
                opts.Create<JackProjectTemplateAbilityModule>();
                opts.AddControllerInContainer(true);
            });

            Configure<Microsoft.AspNetCore.Http.Json.JsonOptions>(opts =>
            {
                opts.SerializerOptions.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull;
                opts.SerializerOptions.Converters.Add(System.Text.Json.Serialization.Metadata.JsonMetadataServices.TimeSpanConverter);
                opts.SerializerOptions.Converters.Add(JSON.DefaultStringEnumConverter);
            });

            #region 配置日志
            LoggerConfiguration loggerConfiguration = new();
            loggerConfiguration
                .MinimumLevel.Override("Default", Serilog.Events.LogEventLevel.Information)
                .MinimumLevel.Override("Microsoft", Serilog.Events.LogEventLevel.Warning)
                .MinimumLevel.Override("Microsoft.Hosting.Lifetime", Serilog.Events.LogEventLevel.Information)
                .MinimumLevel.Override("Microsoft.AspNetCore.Hosting.Diagnostics", Serilog.Events.LogEventLevel.Warning)
                .MinimumLevel.Override("Microsoft.AspNetCore.Routing.EndpointMiddleware", Serilog.Events.LogEventLevel.Warning)
                .MinimumLevel.Override("Microsoft.EntityFrameworkCore.Database.Command", Serilog.Events.LogEventLevel.Information)
                //.MinimumLevel.Override("Microsoft.EntityFrameworkCore", Serilog.Events.LogEventLevel.Information)
                .Enrich.FromLogContext()
                .Enrich.WithEnvironmentName()
                .Enrich.WithMachineName()
                .Enrich.WithEnvironmentUserName()
                .WriteTo.Console()
#if DEBUG
                .WriteTo.Debug()
#endif
                ;

            var esUriSection = configurationManager.GetSection("Logging:ElasticSearchLog:Uris");
            if (esUriSection != null)
            {
                string[]? uris = esUriSection.Get<string[]>();
                if (uris != null)
                {
                    string? indexFormatPrefix = configurationManager.GetSection("Logging:ElasticSearchLog:IndexFormatPrefix").Get<string>();
                    ArgumentNullException.ThrowIfNullOrWhiteSpace(indexFormatPrefix, "Logging:ElasticSearchLog:IndexFormatPrefix");

                    loggerConfiguration.WriteTo.Elasticsearch(new Serilog.Sinks.Elasticsearch.ElasticsearchSinkOptions(uris.Select(uri => new Uri(uri)))
                    {
                        BatchPostingLimit = 100,
                        IndexFormat = $"{indexFormatPrefix}-{webHostEnvironment.EnvironmentName?.ToLower()}" + "-{0:yyyy.MM.dd}",
                        QueueSizeLimit = 1_000_000,
                        TemplateName = "serilog-events-index-template",
                    });
                }
            }

            Log.Logger = loggerConfiguration.CreateLogger();
            context.Services.AddSerilog();

            context.Services.AddHttpLogging(logging =>
            {
                logging.LoggingFields = HttpLoggingFields.All;
                logging.RequestBodyLogLimit = 4096;
                logging.ResponseBodyLogLimit = 4096;
            });
            #endregion
        }

        public override void OnApplicationInitialization(ApplicationInitializationContext context)
        {
            ArgumentNullException.ThrowIfNull(context);

            var app = context.GetWebApplication();
            ArgumentNullException.ThrowIfNull(app);

            // Configure the HTTP request pipeline.
            if (!app.Environment.IsProduction())
            {
                app.UseDeveloperExceptionPage();
                app.UseSwagger();
                app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/JackProjectTemplateV1/swagger.json", "新总运营后台-管理后台-接口 v1"));

                app.UseKnife4UI(c =>
                {
                    c.RoutePrefix = string.Empty;
                    c.SwaggerEndpoint("/JackProjectTemplateV1/api-docs", "新总运营后台-管理后台-接口 v1");
                    c.ShowCommonExtensions();
                    c.ShowExtensions();
                    c.DisplayRequestDuration();
                    c.DisplayOperationId();
                    c.DefaultModelRendering(ModelRendering.Model);
                });

                app.MapSwagger("{documentName}/api-docs");
            }

            app.UseRouting();
            app.UseCors();

            // 用于健康检查
            app.Map("/hc", () => string.Empty);

            //if (app.Environment.IsProduction())
            //    app.UseHttpsRedirection();

            #region 配置日志
            app.UseSerilogLogContext(setting =>
            {
                setting.EnrichersForContextFactory = httpContext =>
                {
                    return new[]
                    {
                        new PropertyEnricher("Url", httpContext.Request.GetDisplayUrl()),
                        new PropertyEnricher("ApplicationName", app.Environment.ApplicationName),
                        new PropertyEnricher("TraceIdentifier", httpContext.TraceIdentifier),
                        new PropertyEnricher("MachineName", Environment.MachineName),
                        new PropertyEnricher("ClientIP", httpContext.GetClientIPAddress()),
                        new PropertyEnricher("UserAgent", httpContext.Request.Headers.UserAgent.ToString()),
                        new PropertyEnricher("Host", httpContext.Request.Host),
                        new PropertyEnricher("Protocol", httpContext.Request.Protocol),
                        new PropertyEnricher("Scheme", httpContext.Request.Scheme),
                        new PropertyEnricher("HttpMethod", httpContext.Request.Method),
                        new PropertyEnricher("QueryString", httpContext.Request.QueryString),
                        new PropertyEnricher("ContentType", httpContext.Response.ContentType),
                        new PropertyEnricher("EndpointName", httpContext.GetEndpoint()?.DisplayName),
                    };
                };
            });
            //app.UseSerilogRequestLogging(options =>
            //{
            //    options.EnrichDiagnosticContext = (diagnosticContext, httpContext) =>
            //    {
            //        // 设置请求的主机名、协议、方案和查询字符串
            //        diagnosticContext.Set("Host", httpContext.Request.Host);
            //        diagnosticContext.Set("Protocol", httpContext.Request.Protocol);
            //        diagnosticContext.Set("Scheme", httpContext.Request.Scheme);
            //        diagnosticContext.Set("HttpMethod", httpContext.Request.Method);
            //        diagnosticContext.Set("QueryString", httpContext.Request.QueryString);

            //        // 设置响应的内容类型
            //        diagnosticContext.Set("ContentType", httpContext.Response.ContentType);

            //        // 检索选定的端点名称并设置
            //        var endpoint = httpContext.GetEndpoint();
            //        if (endpoint != null)
            //        {
            //            diagnosticContext.Set("EndpointName", endpoint.DisplayName);
            //        }
            //    };
            //});
            //app.UseHttpLogging();

            app.UseWhen(
    context => context.Request.Path.StartsWithSegments("/api"),
    builder => builder.UseHttpLogging());
            #endregion

            app.UseAuthorization();
            app.MapControllers();
        }
    }
}
